import { IRawSession } from "@/chatlog_types/IRawSession";
import { is_raw_session_item_changed } from "@/chatlog_types/is_raw_session_item_changed";
import { ContactType } from "@/constants/ContactType";
import { GlobalValue } from "@/GlobalValue";
import { GhostChatMgr } from "@/mgr/chat/GhostChatMgr";
import Callbacks from "@/utils/Callbacks";
import { Poller } from "@/utils/Poller";
import axios, { isAxiosError } from "axios";
import localforage from "localforage";
import { ISessionItem } from "./ISessionItem";
import { SessionListWraper } from "./SessionListWraper";
import { create_session_commit_job } from "./create_session_commit_job";
import { tasks_keeper } from "../tasks/instance";

export namespace SessionsMgr {
  export interface Callbacks {
    on_loading_changed?(mgr: SessionsMgr, is_loading: boolean): void;
    on_list_change?(mgr: SessionsMgr, value: ISessionItem[], prev: ISessionItem[]): void;
    on_item_change?(mgr: SessionsMgr, value: ISessionItem, prev?: ISessionItem): void;
  }
}

export class SessionsMgr {
  static readonly TAG = 'SessionsMgr';
  readonly callbacks = new Callbacks<SessionsMgr.Callbacks>();
  readonly chatmgrs = new Map<string, GhostChatMgr>()
  readonly list_wrapper = new SessionListWraper(this)
    .on_item_change((a, b) => {
      // 新会话 && (群聊 || 私聊)
      if (
        !b && (
          a.contact_type === ContactType.Value.群聊 ||
          a.contact_type === ContactType.Value.联系人
        )
      ) {
        let chatmgr = this.chatmgrs.get(a.userName)
        if (!chatmgr) this.chatmgrs.set(a.userName, chatmgr = new GhostChatMgr(a))
        const { auto_collect, too_full } = tasks_keeper();
        const should_start = auto_collect && !too_full
        should_start ? chatmgr.start() : chatmgr.stop()
      }
      this.callbacks.emit('on_item_change')(this, a, b)
    })
    .on_list_change((a, b) => this.callbacks.emit('on_list_change')(this, a, b))
  get map(): ReadonlyMap<string, ISessionItem> { return this.list_wrapper.map }
  get list(): ISessionItem[] { return this.list_wrapper.list }

  readonly poller = new Poller(() => this.load()).set_interval(5000)
  get error() { return this.poller.error; };
  get loading() { return this.poller.loading; }

  toggle_chatmgr_enabled() {
    const { auto_collect, too_full } = tasks_keeper();
    const should_start = auto_collect && !too_full
    for (const [, chatmgr] of this.chatmgrs) {
      should_start ? chatmgr.start() : chatmgr.stop()
    }
  }
  constructor() {
    tasks_keeper().callbacks.add({
      on_auto_collect_change: (_) => this.toggle_chatmgr_enabled(),
      on_too_full_change: (_) => this.toggle_chatmgr_enabled()
    })
  }

  async load() {
    const http_addr = GlobalValue.current_state.chatlog_state?.http_addr
    if (!http_addr) return

    const { jid } = this.poller;
    const url = `http://${http_addr}/api/v1/session`
    const params = { format: 'json' }
    this.callbacks.emit('on_loading_changed')(this, true);
    return axios(url, { responseType: 'json', params })
      .then((reply) => {
        if (jid != this.poller.jid) return [];
        if (!reply || !Array.isArray(reply?.data))
          console.warn(`[${SessionsMgr.TAG}.load] 'reply.data' must be array!`)
        const raw_list: IRawSession[] = reply?.data || []
        raw_list.sort((a, b) => b.nOrder - a.nOrder)
        this.list_wrapper.set_raw_list(raw_list);
        return this.list_wrapper.list
      }).catch((e) => {
        return Promise.reject(
          isAxiosError(e) && typeof e.response?.data === 'string' ?
            new Error(`[${e.response.status}]${e.response?.data}`) : e
        );
      }).finally(() => {
        if (jid != this.poller.jid) return;
        this.callbacks.emit('on_loading_changed')(this, false);
      })
  }
  start() {
    this.poller.start();
  }
  stop() {
    this.poller.stop();
    for (const [, chatmgr] of this.chatmgrs)
      chatmgr.stop()
  }
  submit({ raw }: ISessionItem) {
    const id = tasks_keeper().unshift(create_session_commit_job(raw))
    if (!tasks_keeper().auto_submit) tasks_keeper().start(id)
  }
}
let inst: SessionsMgr | null = null
export const sessions_mgr = () => {
  if (!inst) {
    inst = (window as any).sessions_mgr = new SessionsMgr()
    inst.callbacks.add({
      on_item_change(_, { raw, contact_type, contact_type_name }) {
        if (raw.nOrder === 0) return;
        switch (contact_type) {
          case ContactType.Value.服务号:
          case ContactType.Value.工具:
          case ContactType.Value.OPENIM:
            return;
        }
        need_update(raw).then((n) => n && tasks_keeper().push(create_session_commit_job(raw)))
      }
    })

  }
  return inst;
};


export const sessions_forage = localforage.createInstance({ name: 'sessions_forage' })

async function need_update(raw: IRawSession): Promise<boolean> {
  const key = `session_item##${raw.account}##${raw.userName}`;
  return sessions_forage.getItem<IRawSession>(key).then((prev) => {
    if (!prev) return true;
    return is_raw_session_item_changed(raw, prev);
  })
}
